﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Resources;
using System.Reflection;
using System.Timers;
using System.IO;


namespace PixentrationEngine
{
    /// <summary>
    /// Identifies the reason the score event was raised.
    /// </summary>
    /// <remarks>Requirements: 8.1</remarks>
    public enum ScoreType
    {
        /// <summary>
        /// The player scored a matched pair.
        /// </summary>
        Match,
        /// <summary>
        /// The player chose a mismatched pair.
        /// </summary>
        Mismatch,
        /// <summary>
        /// All pairs matched at end of game; bonus.
        /// </summary>
        Bonus,
        /// <summary>
        /// Bonus for remaining seconds on the timer.
        /// </summary>
        TimerBonus,
        /// <summary>
        /// Score reset to zero, such as when game is stopped.
        /// </summary>
        Zeroed
    }

    /// <summary>
    /// Represents operational states of the Pixentration game.
    /// </summary>
    /// <remarks>Requirements: 1.1, 1.2, 1.4, 7.0, 8.3.2</remarks>
    public enum PixGameState
    {
        /// <summary>
        /// The game has not yet been initialized.
        /// </summary>
        Undefined = 0,
        /// <summary>
        /// A new game has been initialized.
        /// </summary>
        Initialized,
        /// <summary>
        /// The game is in progress. This state includes resume after pausing.
        /// </summary>
        Running,
        /// <summary>
        /// The game is in pause mode.
        /// </summary>
        Paused,
        /// <summary>
        /// The game has been stopped by user action. It cannot be resumed.
        /// </summary>
        Stopped,
        /// <summary>
        /// The game has ended, either because time ran out or all matches have been achieved.
        /// </summary>
        Ended,
        /// <summary>
        /// Similar to Ended state but the final score is one of the top ten on record.
        /// </summary>
        EndedWithHighScore
    }
    #region Delegates

    // Event handler definitions. Clients must implement these methods
    // to receive events.
    public delegate void GameTimingEventHandler(object sender, TimingEventArgs e);
    public delegate void GameStateChangedHandler(object sender, StateEventArgs e);
    public delegate void ScoreChangedHandler(object sender, ScoreEventArgs e);

    #endregion

    public class PixentrationGame
    {
        #region Events

        public event GameTimingEventHandler tick;
        public event GameStateChangedHandler stateChanged;
        public event ScoreChangedHandler scoreChanged;

        #endregion
        
        private // fields
        
        // Image list object
        List<Image> imageList;
        // Image used to hide cards.
        Image backImage;

        // Constants
        const int MAX_BOARD_SIZE = 36;
        const int MIN_IMG_WIDTH = 5;
        const int MIN_IMG_HEIGHT = 5;
        const int SCORE_MATCH = 5;
        const int SCORE_MISS = -1;
        const int SCORE_BONUS = 10;
        const int SCORE_PER_SECOND_LEFT = 1;
        const int TIMER_INTERVAL = 1000;

        // Matching array
        // This array keeps track of where matching
        // images are in the image list.
        // '-1' values are used as tombstones for
        // completed matches.
        int[] matchmap;

        // Timing fields
        Timer gameTimer;
        static long timeLeft;

        // Game state field
        PixGameState currentState = PixGameState.Undefined;

        // Scoring
        int currentScore;
        int numMatches;
        int winMatches;

        Scorecard scorecard;

           

        /// <summary>
        /// Initializes the game engine.
        /// </summary> 
        /// <param name="settings">
        /// A PixGameSettings instance that contains
        /// the settings required to initialize the game.
        /// </param>
        /// <remarks>
        /// <para>Requirements: 1.1</para>
        /// <para>Call Initialize after creating a new instance of the
        /// PixentrationEngine class or to start a new game after an existing
        /// game has ended or has been stopped.</para>
        /// <para>Calling this method implicity calls Stop() if the game is currently
        /// running or paused.</para>
        /// <para>Post-condition is game is in Initialized state.</para>
        /// <para>Throws ArgumentOutOfRangeException when board size exceeds maximum allowable
        /// size or board size is not an even number.</para>
        /// <para>Raises the stateChanged event.</para></remarks>
        /// 
        public void Initialize(PixGameSettings settings)
        {

            if (settings.BoardSize > MAX_BOARD_SIZE ||
                settings.BoardSize%2 != 0)
            {
                throw new ArgumentOutOfRangeException();
            }

            if (this.CurrentState == PixGameState.Running ||
                this.CurrentState == PixGameState.Paused)
            {
                this.Stop();
            }

            currentScore = 0;
            numMatches = 0;
            winMatches = settings.BoardSize / 2;

            // Create the matching map array.
            matchmap = new int[settings.BoardSize];

            // Load the images.
            imageList = new List<Image>();

            if (settings.URL == null)
            {
                LoadImagesFromResource(settings.BoardSize / 2);
            }
            else
            {
                LoadImagesFromPath(settings.URL, settings.BoardSize / 2);
            }

            // Fill the array
            ResizeImages(settings.ImageSize);
            CreatePairs();
            ShuffleImageList();

            // Initialize the scorecard
            if (scorecard == null)
            {
                InitScorecard();
            }

            // Set up the timer
            gameTimer = new Timer(TIMER_INTERVAL);
            timeLeft = settings.GameTime;
            gameTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

            // Always start in the Initialized state
            currentState = PixGameState.Initialized;
            OnStateChanged(new StateEventArgs(PixGameState.Initialized));
        }

        /// <summary>
        /// Retrieves a value that indicates the maximum size allowed for the game board.
        /// </summary>
        /// /// <reamrks><para>Requirements: 5.0</para>
        /// <para>Use this value to determine valid values for the BoardSize memeber of the PixGameSettings class.</para> </reamrks>
        int MaxBoardSize
        {
            get
            {
                return MAX_BOARD_SIZE;
            }
        }

        /// <summary>
        /// Initialize the scorecard.
        /// </summary>
        /// <remarks>Requirements: 8.0</remarks>
        private void InitScorecard()
        {
            // Init the scorecard
            scorecard = new Scorecard();
            scorecard.LoadScores();
        }

        /// <summary>
        /// Starts a new game or restarts a paused game.
        /// </summary>
        /// <remarks><para>Requirements: 1.1, 1.5, 5.5, 6.2</para>
        /// <para>The current game state must be Paused or Initialized. Otherwise,
        /// this method has no effect.<para>Raises the stateChanged event.</para></remarks>
        public void Start()
        {
            if (currentState == PixGameState.Paused ||
                currentState == PixGameState.Initialized)
            {
                gameTimer.Start();
                currentState = PixGameState.Running;
                OnStateChanged(new StateEventArgs(PixGameState.Running));                
            }
        }

        /// <summary>
        /// Stops the current game in progress.
        /// </summary>
        /// <remarks><para>Requirements: 1.2, 1.4, 5.5</para>
        /// <para>Calling this method ends the game. It cannot be restarted.</para>
        /// <para>Raises the stateChanged event.</para></remarks>
        public void Stop()
        {
            gameTimer.Stop();
            timeLeft = 0;

            // Create the argument object
            TimingEventArgs Args = new TimingEventArgs(timeLeft);

            if (tick != null)
            {
                // Fire the event.
                tick(this, Args);
            }

            currentState = PixGameState.Stopped;
            OnStateChanged(new StateEventArgs(PixGameState.Stopped)); 
        }

        /// <summary>
        /// Pauses the current game.
        /// </summary>
        /// <remarks>
        /// <para>Requirements: 1.5, 5.5</para>
        /// <para>Pauses the current game. Current game state must be Running, 
        /// otherwise this method has no effect.</para>
        /// <para>Raises the stateChanged event.</para></remarks>
        public void Pause()
        {
            if (currentState == PixGameState.Running)
            {
                gameTimer.Stop();
                currentState = PixGameState.Paused;
                OnStateChanged(new StateEventArgs(PixGameState.Paused));                
            }
        }

        /// <summary>
        /// Retrieve the current score.
        /// </summary>  
        /// <value>Integer conaining the current score.</value>
        /// <remarks>Requirements: 8.2</remarks>
        public int CurrentScore
        {
            get
            {
                return currentScore;
            }
        }

        /// <summary>
        /// Retrieve the image used to hide cards.
        /// </summary>
        /// <value>Image object that contains the card back image.</value>
        /// <remarks>5.0</remarks>
        public Image BackImage
        {
            get
            {
                return backImage;
            }
        }       
   

        /// <summary>
        /// Helper function that handles end-of-game operations.
        /// </summary>
        /// <remarks>Requirements: 5.5, 7.2.3, 8.3</remarks>
        private void GameEnded()
        {
            ScoreEventArgs args = new ScoreEventArgs(ScoreType.Zeroed, 0);

            // Cleared the board!
            gameTimer.Stop(); // Not this.Stop() which resets timeLeft

            // Bonus if time left on timer.
            if (timeLeft > 0)
            {
                currentScore += SCORE_BONUS;
                args.newScore = currentScore;
                args.type = ScoreType.Bonus;
                OnScoreChanged(args);
            }

            // Fire an event for every remaining second.
            while(timeLeft > 0)
            {
                currentScore += (SCORE_PER_SECOND_LEFT);
                args.newScore = currentScore;
                args.type = ScoreType.TimerBonus;
                OnScoreChanged(args);
                timeLeft--;
            }            

            // Determine which event to fire.
            if (IsHighScore())
            {
                currentState = PixGameState.EndedWithHighScore;
                OnStateChanged(new StateEventArgs(PixGameState.EndedWithHighScore));               
            }
            else
            {
                currentState = PixGameState.Ended;
                OnStateChanged(new StateEventArgs(PixGameState.Ended));
            }
        }

        /// <summary>
        /// Retrieve the current game state.
        /// </summary>
        /// <value>A PixGameState value that contains the current game state.</value>
        /// <remarks>Requirements: 1.1, 1.2, 1.4, 7.0, 8.3.2</remarks>
        public PixGameState CurrentState
        {
            get
            {
                return currentState;
            }
        }

        /// <summary>
        /// Tests the current score against the scorecard
        /// to determine if the current score belongs on the high
        /// scores list. Used to determine whether to set the EndedWithHighScore
        /// game state.
        /// </summary>
        /// <returns>Boolean indicating whether the current score is one of the top ten
        /// high scores.</returns>
        /// <remarks>Requirements: 8.3</remarks>
        private Boolean IsHighScore()
        {
            ScoreEntry se = new ScoreEntry();
            se.Score = currentScore;            
            return scorecard.isHighScore(se);
        }

        /// <summary>
        /// <para>Retrieves a list of images.
        /// Images in the list are pre-sized as specified
        /// during initialization.
        /// 
        /// </summary>
        /// <returns>A collection of Image objects as an ArrayList.</returns>
        /// <remarks>
        /// <para>Requirements: 5.1</para>
        /// <para>Images are in random order.
        /// Each image is duplicated. That is, there is a pair
        /// of each image in the collection.</para>
        /// <para>You can call this method anytime, but the return value may
        /// be NULL or empty if you have not called Initialize().</para>
        /// <para>Do not change the order of images in the collection! 
        /// Image order is key to determining matches.</para></remarks>
        public List<Image> GetImages()
        {
            return imageList;
        }

        /// <summary>
        /// Retrieves the current list of high scores, for display purposes.
        /// </summary>
        /// <returns>An ArrayList that contains the high scores. Each score is contained
        /// by a ScoreEntry object.</returns>
        /// <remarks><para>Requirements: 8.3</para>
        /// <para>You can call this function even if the game has not been initialized.</para></remarks>
        public List<ScoreEntry> GetScores()
        {
            if (scorecard == null)
            {
                InitScorecard();
            }
            return scorecard.GetScores();
        }

        /// <summary>
        /// Sets the new high score name.
        /// Current game state must be EndedWithHighScore.
        /// </summary>
        /// <param name="name">String containing the name.</param>
        /// <returns>Boolean indicating whether the high score was accepted.</returns>
        /// <remarks>Requirements: 8.3.2</remarks>
        public Boolean SetHighScoreName(String name)
        {
            if (currentState == PixGameState.EndedWithHighScore)
            {
                ScoreEntry se = new ScoreEntry();
                se.Score = currentScore;
                se.Name = name;

                Boolean ret = scorecard.NewScore(se);

                if (ret == true)
                {
                    scorecard.SaveScores();
                }
            }
            else
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Method to determine whether two images in the image list
        /// are a match, by index.
        /// </summary>
        /// <param name="first">Index of first image in image list.</param>
        /// <param name="second">Index of second image in image list.</param>
        /// <returns>Boolean containing true if the indexes contain matching images; otherwise false.</returns>
        /// <remarks>
        /// <para>Requirements: 7.2.1, 8.1.2, 8.1.3</para>
        /// <para>Throws ArgumentException if the paramaters are out of range. Throws InvalidOperationException
        /// if the current game state is not Running. Returns false if the match has been previously tested and found
        /// to be a true match.</para>
        /// <para>Raises the scoreChanged event if match is true.</para>
        /// <para>Immediately ends the game if the match is the final pair to be matched.</para></remarks>
        public Boolean TestMatch(int first, int second)
        {
            // No match if game is not in progress.
            if (this.CurrentState != PixGameState.Running &&
                this.CurrentState != PixGameState.Paused)
            {
                return false;
            }

            // Validate parameters
            if (first == second ||
                first < 0 ||
                second < 0 ||
                first > MAX_BOARD_SIZE -1 ||
                second > MAX_BOARD_SIZE -1)
            {
                throw new ArgumentException();
            }

            ScoreEventArgs args = new ScoreEventArgs(ScoreType.Zeroed, 0);

            if (matchmap[first] == -1 ||
               matchmap[second] == -1)
                return false;
      
            if (matchmap[first] == matchmap[second])
            {
                // Tombstone the matched images
                matchmap[first] = -1;
                matchmap[second] = -1;

                // Scoring
                currentScore += SCORE_MATCH;
                args.newScore = currentScore;
                args.type = ScoreType.Match;
                OnScoreChanged(args);

                numMatches++;

                if (numMatches == winMatches)
                { 
                    // Board is cleared.
                    GameEnded();
                }

                return true;
            }

            currentScore += SCORE_MISS;

            if (currentScore < 0)
                currentScore = 0;

            args.newScore = currentScore;
            args.type = ScoreType.Mismatch;

            OnScoreChanged(args);           

            return false;
        }

        /// <summary>
        /// Private method to load image files from embedded resources.
        /// </summary>
        /// <param name="cImages">An int containing the number of images to load.</param>
        /// <remarks>
        /// <para>Requirements: 4.2.1</para>
        /// <para>Code assumes that embedded resources are available in sufficient
        /// number to equal MAX_BOARD_SIZE / 2. This is a contract.</para></remarks>
        private void LoadImagesFromResource(int cImages)
        {
            Assembly thisExe = Assembly.GetExecutingAssembly();
            string[] resources = thisExe.GetManifestResourceNames();
            int count = 0;

            // Loop through the resource names
            foreach (string resource in resources)
            {
                // We only want JPEG images
                if (resource.Contains(".jpg") && !resource.Contains("Back.jpg"))
                {
                    if (count != cImages)
                    {
                        System.IO.Stream file = thisExe.GetManifestResourceStream(resource);
                        Image img = Image.FromStream(file);
                        img.Tag = resource;
                        imageList.Add(img);
                        
                        // update the match map
                        matchmap[imageList.Count - 1] = imageList.Count - 1 ;
                        count++;
                    }
                }
                else if(resource.Contains("Back.jpg"))
                {
                    System.IO.Stream file = thisExe.GetManifestResourceStream(resource);
                    backImage = Image.FromStream(file);
                    backImage.Tag = resource;                   
                }
            }
        }

        /// <summary>
        /// Helper function to retrieve the card back image from the embedded resources.
        /// Card back image name must be "Back.jpg".
        /// </summary>
        /// <remarks>Requirements: 7.1</remarks>
        private void LoadBackImageFromResource()
        {
            Assembly thisExe = Assembly.GetExecutingAssembly();
            string[] resources = thisExe.GetManifestResourceNames();
  
            // Loop through the resource names
            foreach (string resource in resources)
            {
                if (resource.Contains("Back.jpg"))
                {
                    System.IO.Stream file = thisExe.GetManifestResourceStream(resource);
                    backImage = Image.FromStream(file);
                    backImage.Tag = resource;
                    break;
                }
            }
        }

        /// <summary>
        /// Loads image list from a folder
        /// </summary>
        /// <param name="path">Path to the directory that contains the images</param>
        /// <param name="cImages">Count of images to load.</param>
        /// <remarks>
        /// <para>Requirements: 4.2.2</para>
        /// <para>This method can load files of type JPEG, PNG, BMP, TIFF, and GIF.
        /// If there are fewer available images than specified in the cImages parameter, this method
        /// then loads from resources.</para></remarks>
        private void LoadImagesFromPath(String path, int cImages)
        {           
            string[] fileList = Directory.GetFiles(path);
            int pos = 0;
            

            for (int i = 0; i < fileList.Length; i++)
            {
                if (fileList[i].Contains(".jpg") ||
                    fileList[i].Contains(".png") ||
                    fileList[i].Contains(".bmp") ||
                    fileList[i].Contains(".tif") ||
                    fileList[i].Contains(".gif"))
                {
                    if (pos < cImages)
                    {
                        // Get the image and add it to the list.
                        Image img = Image.FromFile(fileList[i]);
                        img.Tag = Path.GetFileName(fileList[i]).ToString();
                        imageList.Add(img);
                        // Put the index into the match map.
                        matchmap[pos] = pos;
                        pos++;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            if (imageList.Count < cImages)
            {
                // Load remaining images from resources.                
                LoadImagesFromResource(cImages - imageList.Count);
            }

            // Get the image for the card backs.
            LoadBackImageFromResource();
        }


        /// <summary>
        /// Resizes images in the image list to the specified dimensions.
        ///
        /// </summary>
        /// <param name="size">Contains the new image size, in pixels.</param>
        /// <remarks>
        /// <para>Requirements: 4.2.2, 5.6</para>
        /// <para>New size must be greater than MIN_IMG_HEIGHT x MIN_IMG_WIDTH.</para></remarks>
        private void ResizeImages(Size size)
        {
            if (size.Height < MIN_IMG_HEIGHT || size.Width < MIN_IMG_WIDTH)
                throw new ArgumentOutOfRangeException();

            for (int i = 0; i < imageList.Count; i++)
            {
                string tag = imageList[i].Tag.ToString();
                Image img = imageList[i];
                    
                Bitmap b = new Bitmap(size.Width, size.Height);
                Graphics g = Graphics.FromImage((Image)b);
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;

                g.DrawImage(img, 0, 0, size.Width, size.Height);
                g.Dispose();

                imageList[i] = (Image)b;
                imageList[i].Tag = tag;
            }

            if(backImage != null)
            {
                // Resize the image used to hide cards.
                string tag2 = backImage.Tag.ToString();
                Image img2 = backImage;

                Bitmap b2 = new Bitmap(size.Width, size.Height);
                Graphics g2 = Graphics.FromImage((Image)b2);
                g2.InterpolationMode = InterpolationMode.HighQualityBicubic;

                g2.DrawImage(img2, 0, 0, size.Width, size.Height);
                g2.Dispose();

                backImage = (Image)b2;
                backImage.Tag = tag2;
            }
                                            
        }

        /// <summary>
        /// Creates a duplicate image for each image in the image list.
        /// Each new image is appended to the end of the list.
        /// For example, a list that contained 1,2,3,4
        /// will now contain 1,2,3,4,1,2,3,4.
        /// </summary>
        /// <remarks>
        /// <para>Requirements: 5.4</para>
        /// </remarks>
        private void CreatePairs()
        {
            int count = imageList.Count;

            for (int i = 0; i < count; i++)
            {
                string tag = imageList[i].Tag.ToString();
                Image img = imageList[i];
                Bitmap b = new Bitmap(img.Width, img.Height);
                Graphics g = Graphics.FromImage((Image)b);
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;

                g.DrawImage(img, 0, 0, img.Width, img.Height);
                g.Dispose();

                Image newImg = Image.FromHbitmap(b.GetHbitmap());
                newImg.Tag = tag;

                imageList.Add(newImg);

                // update the matchmap
                matchmap[imageList.Count - 1] = i;
            }
        }

        /// <summary>
        /// Randomizes the order of the image list similar to
        /// shuffling a deck of cards. Maintains the identical
        /// swapping in the matching array.
        /// </summary>
        /// <remarks>
        /// <para>Requirements: 7.1</para>
        /// </remarks>
        void ShuffleImageList()
        {
            // For every image in the list, do 2 random swaps
            // That yields a nice, random shuffle.
            // Too many swaps are a performance hit, especially with
            // large images.
            Random rand = new Random();

            for (int i = 0; i < imageList.Count*2; i++)
            {
                // Pick 2 at random and swap them.
               

                // Generate two indices to swap at random.
                int first = rand.Next(0, imageList.Count);
                int second = rand.Next(0, imageList.Count);

                // Cache the first image
                // We can't just cache image references
                // We have to actually get the bitmap
                Image img1 = imageList[first];
                string tag = imageList[first].Tag.ToString();
                Bitmap b = new Bitmap(img1.Width, img1.Height);
                Graphics g = Graphics.FromImage((Image)b);
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;

                g.DrawImage(img1, 0, 0, img1.Width, img1.Height);
                g.Dispose();

                Image newImg1 = Image.FromHbitmap(b.GetHbitmap());
                newImg1.Tag = tag;
                b.Dispose();

                // Cache the second image.
                Image img2 = imageList[second];
                tag = imageList[second].Tag.ToString();
                Bitmap b2 = new Bitmap(img2.Width, img2.Height);
                Graphics g2 = Graphics.FromImage((Image)b2);
                g2.InterpolationMode = InterpolationMode.HighQualityBicubic;

                g2.DrawImage(img2, 0, 0, img2.Width, img2.Height);
                g2.Dispose();

                Image newImg2 = Image.FromHbitmap(b2.GetHbitmap());
                newImg2.Tag = tag;
                b2.Dispose();

                // Do the swap.
                imageList[first] = newImg2;
                imageList[second] = newImg1;

                // Do the swap in the matching array, too.
                // In this case, the integer isn't a reference,
                // so the swapping is easy.
                int iTemp = matchmap[first];
                matchmap[first] = matchmap[second];
                matchmap[second] = iTemp;
            }
        }

        /// <summary>
        /// Timer event handler
        /// </summary>
        /// <param name="source">The object that raised the event.</param>
        /// <param name="e">Event argument, includes SignalTime property.</param>
        /// <remarks>Requirements: 5.5, 6.2</remarks>
        private  void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            // One less second left in the game
            timeLeft--;

            // Create the argument object
            TimingEventArgs Args = new TimingEventArgs(timeLeft);

            if (tick != null)
            {
                // Fire the event.
                tick(this, Args);
            }

            if (timeLeft == 0)
            {
                GameEnded();
            }
        }

        /// <summary>
        /// Event for game state changes
        /// </summary>
        /// <param name="e">Game state</param>
        /// <remarks>Requirements: 1.1, 1.2, 1.4, 7.0, 8.3.2</remarks>
        protected virtual void OnStateChanged(StateEventArgs e)
        {
            if (stateChanged != null)
            {
                stateChanged(this, e);
            }
        }

        /// <summary>
        /// Event for score changes
        /// </summary>
        /// <param name="e">Score</param>
        /// <remarks>Requirements: 8.1</remarks>
        protected virtual void OnScoreChanged(ScoreEventArgs e)
        {
            if (scoreChanged != null)
            {
                scoreChanged(this, e);
            }
        }

        /// <summary>
        /// Destructor. Saves scorecard.
        /// </summary>
        ~PixentrationGame()
        {
            scorecard.SaveScores();
        }
    }

    /// <summary>
    /// Argument for timer event.
    /// </summary>
    /// <remarks>Requirements: 5.5</remarks>
    public class TimingEventArgs : EventArgs
    {
        public long timeLeft;

        public TimingEventArgs(long t)
        {
            timeLeft = t;
        }
    }

    /// <summary>
    /// Argument for state changed event.
    /// </summary>
    /// <remarks>Requirements: 1.1, 1.2, 1.4, 7.0, 8.3.2</remarks>
    public class StateEventArgs : EventArgs
    {
        public PixGameState state;

        public StateEventArgs(PixGameState s)
        {
            state = s;
        }
    }

    /// <summary>
    /// Argument for score changed event.
    /// </summary>
    /// <remarks>Requirements: 8.1</remarks>
    public class ScoreEventArgs : EventArgs
    {
        public ScoreType type;
        public int newScore;

        public ScoreEventArgs(ScoreType t, int s)
        {
            newScore = s;
            type = t;
        }
    }
}

